﻿using SoundEngine3.MIDI.Meta;
using System.Collections.Generic;

namespace ZquenceStudio3Host.DspPlugins.Synthesizers.Nodesizer.Engine
{
    public class MidiMessageHandler
    {
        public struct CurrentMidi
        {
            public int VoiceIndex;
            public MidiMessage Message;
        }

        private Dictionary<int, List<CurrentMidi>> mInternal = new Dictionary<int, List<CurrentMidi>>();
        private Dictionary<int, List<CurrentMidi>> mOutput = new Dictionary<int, List<CurrentMidi>>();
        private int mNextVoice;
        private byte[] mHolds = new byte[128];

        int Hold()
        {
            for (int i = 0; i < 128; i++)
            {
                if (mHolds[i] == 0)
                {
                    mHolds[i] = 1;
                    return i;
                }
            }

            return 0;
        }

        void Release(int i)
        {
            mHolds[i] = 0;
        }
        
        /// <summary>
        /// Return an dictionary with a key as a voice index, and a collection of midis as value.
        /// </summary>
        /// <param name="messages"></param>
        /// <returns></returns>
        public Dictionary<int, List<CurrentMidi>> ProcessMidi(IList<MidiMessage> messages) // TODO: Ifall man är i en grup och skaopar en modul, så hamnar den längst ut :O
        {
            //mOutput.Clear();
            foreach (var output in mOutput)
            {
                output.Value.Clear();
            }
            
            for (int i = 0; i < messages.Count; i++)
            {
                var message = messages[i];

                if (message.MidiData.Status == MidiStatus.NOTE_ON)
                {
                    // Hold.
                    var currentMidi = new CurrentMidi { VoiceIndex = Hold(), Message = message };

                    // Den voicen med dom midisarna ska matas ut.
                    if (!mInternal.ContainsKey(currentMidi.VoiceIndex))
                    {
                        mInternal.Add(currentMidi.VoiceIndex, new List<CurrentMidi>());
                    }
                    mInternal[currentMidi.VoiceIndex].Add(currentMidi);

                    if (!mOutput.ContainsKey(currentMidi.VoiceIndex))
                    {
                        mOutput.Add(currentMidi.VoiceIndex, new List<CurrentMidi>());
                    }
                    mOutput[currentMidi.VoiceIndex].Add(currentMidi);

                    mNextVoice++;
                }
                else if (message.MidiData.Status == MidiStatus.NOTE_OFF)
                {
                    if (mNextVoice <= 0)
                    {
                        // Mey got an note off first of all. Example when user change dsp program while a note is playing.
                        continue;
                    }

                    // Leta efter sin note.
                    foreach (var item in mInternal)
                    {
                        for (int j = item.Value.Count - 1; j >= 0; j--)
                        {
                            var currentMidi = item.Value[j];
                            if (currentMidi.Message.MidiData.Parameter1 == message.MidiData.Parameter1)
                            {
                                // Release.
                                Release(item.Value[j].VoiceIndex);

                                item.Value.RemoveAt(j);

                                currentMidi.Message = message;
                                if (!mOutput.ContainsKey(currentMidi.VoiceIndex))
                                {
                                    mOutput.Add(currentMidi.VoiceIndex, new List<CurrentMidi>());
                                }
                                mOutput[currentMidi.VoiceIndex].Add(currentMidi);
                            }
                        }
                    }

                    mNextVoice--;
                }
            }

            return mOutput;
        }
    }
}
